package com.cuiweiyou.vigilesmicrecord.util.comm;

import android.content.Context;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class UncaughtExceptionUtil implements Thread.UncaughtExceptionHandler {
    private static UncaughtExceptionUtil sInstance = new UncaughtExceptionUtil();

    // 系统默认的异常处理（默认情况下，系统会终止当前的异常程序）
    private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    private Context ctx;
    String dirPath;

    private UncaughtExceptionUtil() {
    }

    public static UncaughtExceptionUtil getInstance() {
        return sInstance;
    }

    // 这里主要完成初始化工作
    public void init(Context context, String dirPath) {
        this.ctx = context;
        this.dirPath = dirPath;
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的异常处理器
        Thread.setDefaultUncaughtExceptionHandler(this); // 将当前实例设为系统默认的异常处理器
    }

    /**
     * 这个是最关键的函数，当程序中有未被捕获的异常，系统将会自动调用#uncaughtException方法
     * thread为出现未捕获异常的线程，ex为未捕获的异常，有了这个ex，我们就可以得到异常信息。
     * 在哪个线程发生异常，uncaughtException()在哪个线程里被调用
     */
    @Override
    public void uncaughtException(Thread thread, Throwable e) {
        try {
            handleException(thread, e);
        } catch (Exception ee) {
            // 防止handleException中出现异常，导致uncaughtException无限循环调用
            ee.printStackTrace();
        }

        finishHandle(thread, e);
    }

    private void handleException(Thread thread, Throwable e) {
        // 打印出当前调用栈信息
        logDebugInfo(e);

        String ymdhms = DateFormatUtil.millis2yyyyMMddHHmmss(System.currentTimeMillis());
        File dir = new File(dirPath);
        if (!dir.exists()) {
            dir.mkdir();
        }
        String fileName = "exception_" + ymdhms + ".txt";
        // 以当前时间创建log文件
        File file = new File(dirPath + File.separator + fileName);
        if (file.exists()) {
            file.delete();
        }
        Log.e("ard", "fileName:" + file.getAbsolutePath());

        try {
            file.createNewFile();
            dumpExceptionToSDCard(file, e);
        } catch (IOException e1) {
            e.printStackTrace();
        }
    }

    private void finishHandle(Thread thread, Throwable e) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            if (mDefaultCrashHandler != null) {
                // 如果系统提供了默认的异常处理器，则交给系统去结束我们的程序，否则就由我们自己结束自己
                Log.e("ard", "使用系统提供的默认异常处理器");
                mDefaultCrashHandler.uncaughtException(thread, e);
            } else {
                Log.e("ard", "直接退出");
                // 直接退出
                // ActivityStack.clearStack();
                Process.killProcess(Process.myPid());
                // System.exit(0);
            }
        } else {
            // 在正式包中，如果非UI线程异常，不退出App
            Log.e("ard", "非UI线程，不退出应用");
        }
    }

    private void dumpExceptionToSDCard(final File f, Throwable ex) throws IOException {
        String error = ex.getMessage();

        StringBuilder bug = new StringBuilder();
        bug.append("error=" + error);

        File dir = new File(f.getParent());
        if (!dir.exists()) {
            dir.mkdirs();
        }

        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(f)));
            pw.println(bug.toString());
            pw.println();
            ex.printStackTrace(pw);// 导出异常的调用栈信息

            pw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (Looper.myLooper() == Looper.getMainLooper()) {
            Toast.makeText(ctx, "请查看 " + f.getAbsolutePath() + " 文件内容并反馈", Toast.LENGTH_LONG).show();
        }
    }

    private void logDebugInfo(Throwable ex) {
        Log.e("ard", getClass().getSimpleName() + " caughtException in ");
        try {
            Log.e("ard", "show crash log in my way ...");
            if (ex.getCause() != null) {
                Log.e("ard", "crashHandler " + ex.getCause().toString());
            }
            StackTraceElement[] stackTraces = ex.getStackTrace();
            for (StackTraceElement element : stackTraces) {
                Log.e("ard", "crashHandler:" + element.getClassName() + "(" + element.getLineNumber() + ") " + element.getMethodName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 打印出当前调用栈信息
        ex.printStackTrace();
    }
}